package aspecto;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Set;


public aspect MiAspecto {
	
	public HashMap<String, Object> varObj = new HashMap<String, Object>();
	
	pointcut setReference(Object target, Object newValue):
		call(* set*(*)) && target(target) && args(newValue);

	before(Object target, Object newValue) : setReference(target,newValue){
		String varNombre = thisJoinPoint.getSignature().getName();
		this.agregarNuevaReferencia(varNombre,newValue);
		this.avisarReferenceCountCambio(varNombre,newValue);
		this.generarReporte();
	}

	 /**
	  * Asigna un valor con una variable, si el valor de la variable es nuevo lo reemplaza, si es el mismo no hace nada
	  */
     public void agregarNuevaReferencia(String nombre,Object obj){
    	 if(! varObj.containsKey(nombre) || (! varObj.get(nombre).equals(obj))){
    			 varObj.put(nombre, obj);
    	 }
     }
     
     /**
      * Devuelve las variables que apuntan al objeto val
      * @param obj
      * @return
      */
     public String variablesAsignadas(Object obj){
    	 String varsActuales = "";
    	 Set<String> vars = this.varObj.keySet();
    	 for(String var : vars){
    		 if(this.varObj.get(var).equals(obj)){
    			 varsActuales = varsActuales +" "+ nombre(var);
    		 }
    	 }
    	 return varsActuales;
     }
     
     public void a(){
    	 
     }
     
     /**
      * Genera el reporte de cuantas variables se le asignaron aun objeto obj
      * @param obj
      */
     public void generarReporte(){
    	 System.out.println("Reporte:");
    	 List<Object> vals = sinRepetidos(this.varObj.values());
    	 for(Object obj : vals){		 
    		 System.out.println(obj.toString()+" esta siendo referenciado por las variables "+this.variablesAsignadas(obj));
    	 }
    	 System.out.println(" ");
     }
     
     /**
      * Notifica cuando se settea una variable
      * @param varNombre
      * @param newValue
      */
     private void avisarReferenceCountCambio(String varNombre, Object newValue) {
    	 System.out.println(newValue.toString()+" fue asignado a la variable "+nombre(varNombre)); 
     }
     
   //Metodos Auxiliares
     
     public static String nombre(String setter){
    	 String s = setter;
 		 String o = s.substring(3);
 		 return o;
     }
     
     public static List<Object> sinRepetidos(Collection<Object> col){
    	 List<Object> set = new ArrayList<Object>();
    	 for(Object o : col){
    		 if(! set.contains(o)){
    			 set.add(o);
    		 }
    	 }
    	 return set;
     }
}
